import re  # 在Python中需要通过正则表达式对字符串进行匹配的时候，需导入模块re


# 使用match函数re.match(正则表达式,要匹配的字符串):match函数从正则和字符串的开头开始匹配，若正则表达式被匹配完就成功然后可用group()来提取数据，若失败就返回None则不能用group()。
# group()默认会把所有匹配的字符串提取出来，但也可以传入参数来控制提取匹配成功的字符串中第几个分组（分组号从1开始）

def signal_match():  # 匹配单个字符的正则表达式    .  []  \d  \D  \s  \S  \w  \W
    ret = re.match(".", "M")  # .匹配任意1个字符（除了\n）
    print(ret.group())
    ret = re.match("豆.菜", "豆芽菜")
    print(ret.group())
    ret = re.match("[h]", "hello")  # []匹配[]中列举的字符
    print(ret.group())
    ret = re.match("[hH]", "Hello")
    print(ret.group())
    ret = re.match("[0-9]Hello", "6Hello")  # []的写法。匹配0-9:[0-9]、匹配0-3和5-9：[0-35-9]、匹配a-z和A-Z：[a-zA-Z]
    print(ret.group())
    ret = re.match(r"嫦娥\d号", "嫦娥1号发射成功")  # \d匹配数字（decimal）即0-9
    print(ret.group())

    # 另外  \D匹配非数字（即不是数字）   \s匹配空白（即空格：tab、space）   \S匹配非空白     \w匹配（即a-z、A-Z、0-9、_、汉字 ）     \W匹配\w之外的


def more_match():  # 匹配多个字符的正则表达式    *  +  ？  {}  { ，}
    ret = re.match("[A-Z][a-z]*", "MnnM")  # *匹配前一个字符(此处为[a-z])出现0次或者无限次（即可有可无）。默认是贪婪（能匹配多少匹配多少）
    print(ret.group())
    ret = re.match("[A-Z]a+", "Maaaaaaaa")  # +匹配前一个字符（此处为a）出现1次或者无限次（即至少有1次）。默认是贪婪（能匹配多少匹配多少）
    print(ret.group())
    ret = re.match(r"[0-9]?[0-9]", "7")  # ?匹配前一个字符出现1次或者0次（即要么有1次，要么没有）。（此时为了保证正则表达式能匹配成功则？前的字符匹配0次）
    print(ret.group())

    # 另外   {m}匹配前一个字符m次(必须为m次)     {m,n}匹配前一个字符m至n次默认是贪婪
    # 贪婪：只要不影响匹配成功，在从左到右的顺序匹配时，会尽量“抓取”最长字符串
    # 非贪婪只要不影响匹配成功匹配的越少越好
    # 另外在*,+,?、{m，n}后面加？使贪婪变成非贪婪
    # 例一匹配电话号码
    s = "This is a number 234-235-22-423"
    r = re.match(r".+(\d+-\d+-\d+-\d+)", s)  # 贪婪下
    print(r.group(1))
    r = re.match(r".+?(\d+-\d+-\d+-\d+)", s)  # 改进非贪婪下
    print(r.group(1))
    # 例二
    print(re.match(r"aa(\d+)", "aa2343ddd").group(1))
    print(re.match(r"aa(\d+?)", "aa2343ddd").group(1))


def star_end_match():  # 匹配开头结尾的正则表达式      ^  $
    # 符合163的邮箱找出来
    email_list = ["xiaoWang@163.com", "xiaoWang@163.comheihei", ".com.xiaowang@qq.com"]
    for email in email_list:
        ret = re.match(r'\w{4,20}@163\.com$', email)  # 这里对.加\表转义（即这里是普通的.而不是正则中的符号.）$表示要匹配的字符串必须到此结束，否则匹配失败。
        if ret:
            print(f'{ret.group()}是正确的邮箱{email}')
        else:
            print(f'{email}邮箱地址不正确')
    # 另外^表示要匹配的字符串必须以^后开始匹配，这并不用于match中，因为match函数就是正则和字符串的开头开始匹配。^对于re中的其它函数有用
    ret = re.match(r"^maa\d*", "maa11123")  # 等价于ret = re.match(r"maa\d*", "maa11123")
    print(ret.group())


def group_match():  # 匹配分组的正则表达式    |  ()  \分组号   (?P<别名>内容)和(?P=别名)：给分组起别名和引用分组
    # |表示匹配左右任意一个表达式都可以，当前面的表达式可以匹配时优先匹配前面的。
    # 匹配0到100
    ret = re.match(r"[1-9]?\d$|100", "78")
    if ret:
        print(ret.group())
    else:
        print('不是0-100之间')
    # ()表示将括号中的字符作为一个分组，分组从左到右依次编号（从1开始）。
    ret = re.match(r"\w{4,20}@(163|qq)\.com", "test@qq.com")
    print(ret.group())
    print(ret.group(1))
    # \分组号表示这里应于第几个分组中的内容相同（即引用了前面的分组）
    # 匹配正确的标签
    labels = ["<html><h1>www.cskaoyan.com</h1></html>", "<html><h1>www.cskaoyan.com</h2></html>"]
    for label in labels:
        ret = re.match(r"<(\w*)><(\w*)>.*</\2></\1>", label)
        if ret:
            print("%s 是符合要求的标签" % ret.group())
        else:
            print("%s 不符合要求" % label)
    # (?P<别名>内容)和(?P=别名)    当分组太多可以通过这种方式给分组起别名，然后引用分组
    for label in labels:
        ret = re.match(r"<(?P<aaa>\w*)><(?P<bbb>\w*)>.*</(?P=bbb)></(?P=aaa)>", label)
        if ret:
            print("%s 是符合要求的标签" % ret.group())
        else:
            print("%s 不符合要求" % label)
    # 另外一种特殊：  [^-]*代表没有遇到小横杠-就一直进行匹配
    ret = re.match(r"([^-]*)-(\d+)", "010-12345678")
    print(ret.group())
    print(ret.group(1))
    print(ret.group(2))


# 使用search函数re.search(正则表达式,要匹配的字符串)只能搜索第一个
def use_search():
    ret = re.search(r"\d+", "阅读次数为 9999,点赞888")
    print(ret.group())


# 使用findall函数re.findall(正则表达式,要匹配的字符串)将搜索到的全部内容形成一个列表返回。无需用group()提取
def use_findall():
    ret = re.findall(r"\d+", "python = 9999, c = 7890, c++ = 12345")
    print(ret)


# 使用finditer函数re.finditer(正则表达式,要匹配的字符串)可以选择一直搜索到第几个内容
def use_finditer():
    matches = re.finditer(r"\d+", "abc123def456ghi789")
    next(matches)  # next关键字的作用相当于返回这一次的匹配结果并跳到下一次的匹配结果，
    second_match = next(matches)  # 这里就返回了第二次的匹配结果
    print(second_match.group())


# 使用sub函数re.sub(正则表达式,替换内容,要匹配的字符串,count=0)将搜索到的内容替换后返回替换后的整个字符串。无需用group()提取
# 其中count参数（替换次数）默认为0即全部替换
def use_sub():
    ret = re.sub(r"\d+", '99', "a=11")
    print(ret)
    ret = re.sub(r"\d+", '99', "a=22,b=33,c=44", 2)  # 替换2次
    print(ret)
    # 替换内容这个参数可传入一个函数
    ret = re.sub(r"\d+", lambda x: str(int(x.group()) + 100), "a=55")  # 这里的x.group()就是搜索到的内容
    print(ret)


# 使用split函数re.split(正则表达式,要匹配的字符串)根据正则表达式来切割字符串，并将所有字符串放到一个列表中返回。
def use_split():
    ret = re.split(r":| ", "info:xiaoZhang 33 shandong")
    print(ret)


# 使用正则的选项：re.A表示不让\w匹配汉字、re.I表示不区分大小写、re.S表示可以让.匹配上\n
def use_option():
    print(re.match(r'\w*', 'abc函', flags=re.A).group())
    print(re.match(r'a*', 'aA', flags=re.I).group())
    print(re.match(r'.*', 'abc\ndef', flags=re.S).group())


# 补充   在正则表达式前加r表示原生字符串，它只服务于\，不对其他进行转义
if __name__ == '__main__':
    signal_match()
    more_match()
    star_end_match()
    group_match()
    use_search()
    use_findall()
    use_finditer()
    use_sub()
    use_split()
    use_option()
